package com.example.juc.sort;

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.*;

@Slf4j
public class SortClient {
    static ExecutorService executorService = Executors.newFixedThreadPool(9);
    static int n = 10;
    static int groupSize = 4;

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        int[] data = getData(n);
        int[] data2 = new int[n];
        System.arraycopy(data, 0, data2, 0, n);

        log.info("原始数组 {}", data);
        long start = System.nanoTime();
        // 多线程分组排序
        multiThreadSort(n, data, groupSize);
        long end = System.nanoTime();
        log.info("多线程耗时 {}", end - start);

        start = System.nanoTime();
        Arrays.sort(data2);
        end = System.nanoTime();
        log.info("单线程耗时 {}", end - start);
    }

    private static void multiThreadSort(int n, int[] data, int groupSize) throws InterruptedException, ExecutionException {
        int[] result = new int[0];
        int[] sorted;
        int index = 0;
        while (index < n) {
            // 当前组
            int[] group;
            if (index + groupSize >= n) {
                group = new int[n - index];
                int temp = 0;
                for (int i = index; i < n; i++) {
                    group[temp++] = data[i];
                }
                index = n;
            } else {
                group = new int[groupSize];
                int temp = 0;
                for (int i = index; i < index + groupSize; i++) {
                    group[temp++] = data[i];
                }
                index = index + groupSize;
            }
            // 当前组排序后
            sorted = sortOneGroup(group);
            // 合并后最新结果
            result = merge(result, sorted);
        }
        log.info("排序完成 {}", result);
        // 关闭线程池
        executorService.shutdown();
    }

    private static int[] sortOneGroup(int[] data) throws InterruptedException, ExecutionException {
        Callable<int[]> task = new Callable<int[]>() {
            @Override
            public int[] call() throws Exception {
                Arrays.sort(data);
                return data;
            }
        };
        Future<int[]> future = executorService.submit(task);
        return future.get();
    }

    private static int[] merge(int[] result, int[] sorted) {
        int n = result.length + sorted.length;
        int[] newArray = new int[n];
        int point1 = 0;
        int point2 = 0;
        int index = 0;
        while (point1 < result.length || point2 < sorted.length) {
            if (point1 == result.length) {
                newArray[index++] = sorted[point2++];
            } else if (point2 == sorted.length) {
                newArray[index++] = result[point1++];
            } else {
                if (result[point1] < sorted[point2]) {
                    newArray[index++] = result[point1++];
                } else {
                    newArray[index++] = sorted[point2++];
                }
            }
        }
        return newArray;
    }

    private static int[] getData(int n) {
        int[] data = new int[n];
        Random random = new Random();
        for (int i = 0; i < n; i++) {
            data[i] = random.nextInt(2 * n);
        }
        return data;
    }
}
